{*
 * Startup code for programs linked with GNU libc, PowerPC64
 * version.
 *
 * Adapted from the glibc-sources (2.16) in the file
 *
 *     sysdeps/powerpc/powerpc64/elf/start.S
 *
 * Original header follows.
 *}

{* Startup code for programs linked with GNU libc.  PowerPC64 version.
   Copyright (C) 1998,1999,2000,2001,2002,2003,2009
   Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   In addition to the permissions in the GNU Lesser General Public
   License, the Free Software Foundation gives you unlimited
   permission to link the compiled version of this file with other
   programs, and to distribute those programs without any restriction
   coming from the use of this file. (The GNU Lesser General Public
   License restrictions do apply in other respects; for example, they
   cover modification of the file, and distribution when not linked
   into another program.)

   Note that people who make modified versions of this file are not
   obligated to grant this special exception for their modified
   versions; it is their choice whether to do so. The GNU Lesser
   General Public License gives permission to release a modified
   version without this exception; this exception also makes it
   possible to release a modified version which carries forward this
   exception.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  *}

{$goto on}

{$ifndef _CALL_ELF or (_CALL_ELF = 1)}
{*
 * "ptrgl" glue code for calls via pointer. This function
 * sequence loads the data from the function descriptor
 * referenced by R11 into the CTR register (function address),
 * R2 (GOT/TOC pointer), and R11 (the outer frame pointer).
 *
 * On entry, R11 must be set to point to the function descriptor.
 *
 * See also the 64-bit PowerPC ABI specification for more
 * information, chapter 3.5.11 (in v1.7).
 *}
procedure ptrgl;  cdecl; public; assembler; nostackframe;
  asm
    ld	  r0, 0(r11)
    std     r2, 40(r1)
    mtctr   r0
    ld      r2, 8(r11)
    ld      r11, 16(r11)
    bctr
  end;
{$endif _CALL_ELF}


{*
 * Function prolog/epilog helpers, which are part of the 64-bit
 * PowerPC ABI.
 *
 * See also the 64-bit PowerPC ABI specification for more
 * information, chapter 3.5.5, "Register saving and restoring
 * function" (in v1.7).
 *}

{* Each _savegpr0_N routine saves the general registers from rN to r31,
 * inclusive. When the routine is called, r1 must point to the start
 * of the general register save area. R0 must contain the old LR on
 * entry.
 *}
label
  _savegpr0_15,
  _savegpr0_16,
  _savegpr0_17,
  _savegpr0_18,
  _savegpr0_19,
  _savegpr0_20,
  _savegpr0_21,
  _savegpr0_22,
  _savegpr0_23,
  _savegpr0_24,
  _savegpr0_25,
  _savegpr0_26,
  _savegpr0_27,
  _savegpr0_28,
  _savegpr0_29,
  _savegpr0_30,
  _savegpr0_31;

procedure _savegpr0_14; cdecl; public; assembler; nostackframe;
  asm
.globl _savegpr0_15
.globl _savegpr0_16
.globl _savegpr0_17
.globl _savegpr0_18
.globl _savegpr0_19
.globl _savegpr0_20
.globl _savegpr0_21
.globl _savegpr0_22
.globl _savegpr0_23
.globl _savegpr0_24
.globl _savegpr0_25
.globl _savegpr0_26
.globl _savegpr0_27
.globl _savegpr0_28
.globl _savegpr0_29
.globl _savegpr0_30
.globl _savegpr0_31
    std r14,-144(r1)
_savegpr0_15:
    std r15,-136(r1)
_savegpr0_16:
    std r16,-128(r1)
_savegpr0_17:
    std r17,-120(r1)
_savegpr0_18:
    std r18,-112(r1)
_savegpr0_19:
    std r19,-104(r1)
_savegpr0_20:
    std r20,-96(r1)
_savegpr0_21:
    std r21,-88(r1)
_savegpr0_22:
    std r22,-80(r1)
_savegpr0_23:
    std r23,-72(r1)
_savegpr0_24:
    std r24,-64(r1)
_savegpr0_25:
    std r25,-56(r1)
_savegpr0_26:
    std r26,-48(r1)
_savegpr0_27:
    std r27,-40(r1)
_savegpr0_28:
    std r28,-32(r1)
_savegpr0_29:
    std r29,-24(r1)
_savegpr0_30:
    std r30,-16(r1)
_savegpr0_31:
    std r31,-8(r1)
    std r0, 16(r1)
    blr
  end;


label
  _restgpr0_15,
  _restgpr0_16,
  _restgpr0_17,
  _restgpr0_18,
  _restgpr0_19,
  _restgpr0_20,
  _restgpr0_21,
  _restgpr0_22,
  _restgpr0_23,
  _restgpr0_24,
  _restgpr0_25,
  _restgpr0_26,
  _restgpr0_27,
  _restgpr0_28,
  _restgpr0_29,
  _restgpr0_31;

{* Each _restgpr0_N routine restores the general registers from rN to r31,
 * inclusive. When the routine is called, r1 must point to the start
 * of the general register save area.
 *}
procedure _restgpr0_14; cdecl; public; assembler; nostackframe;
  asm
.globl _restgpr0_15
.globl _restgpr0_16
.globl _restgpr0_17
.globl _restgpr0_18
.globl _restgpr0_19
.globl _restgpr0_20
.globl _restgpr0_21
.globl _restgpr0_22
.globl _restgpr0_23
.globl _restgpr0_24
.globl _restgpr0_25
.globl _restgpr0_26
.globl _restgpr0_27
.globl _restgpr0_28
.globl _restgpr0_29
    ld r14,-144(r1)
_restgpr0_15:
    ld r15,-136(r1)
_restgpr0_16:
    ld r16,-128(r1)
_restgpr0_17:
    ld r17,-120(r1)
_restgpr0_18:
    ld r18,-112(r1)
_restgpr0_19:
    ld r19,-104(r1)
_restgpr0_20:
    ld r20,-96(r1)
_restgpr0_21:
    ld r21,-88(r1)
_restgpr0_22:
    ld r22,-80(r1)
_restgpr0_23:
    ld r23,-72(r1)
_restgpr0_24:
    ld r24,-64(r1)
_restgpr0_25:
    ld r25,-56(r1)
_restgpr0_26:
    ld r26,-48(r1)
_restgpr0_27:
    ld r27,-40(r1)
_restgpr0_28:
    ld r28,-32(r1)
_restgpr0_29:
    ld r0, 16(r1)
    ld r29,-24(r1)
    mtlr r0
    ld r30,-16(r1)
    ld r31,-8(r1)
    blr
  end;


procedure _restgpr0_30; cdecl; public; assembler; nostackframe;
  asm
    ld r30,-16(r1)
_restgpr0_31:
    ld r0, 16(r1)
    ld r31,-8(r1)
    mtlr 0
    blr
  end;


{* Each _savegpr1_N routine saves the general registers from rN to r31,
 * inclusive. When the routine is called, r12
 * must point to the start of the general register save area.
 *}

label
  _savegpr1_15,
  _savegpr1_16,
  _savegpr1_17,
  _savegpr1_18,
  _savegpr1_19,
  _savegpr1_20,
  _savegpr1_21,
  _savegpr1_22,
  _savegpr1_23,
  _savegpr1_24,
  _savegpr1_25,
  _savegpr1_26,
  _savegpr1_27,
  _savegpr1_28,
  _savegpr1_29,
  _savegpr1_30,
  _savegpr1_31;

procedure _savegpr1_14; cdecl; public; assembler; nostackframe;
  asm
.globl _savegpr1_15
.globl _savegpr1_16
.globl _savegpr1_17
.globl _savegpr1_18
.globl _savegpr1_19
.globl _savegpr1_20
.globl _savegpr1_21
.globl _savegpr1_22
.globl _savegpr1_23
.globl _savegpr1_24
.globl _savegpr1_25
.globl _savegpr1_26
.globl _savegpr1_27
.globl _savegpr1_28
.globl _savegpr1_29
.globl _savegpr1_30
.globl _savegpr1_31

    std r14,-144(r12)
_savegpr1_15:
    std r15,-136(r12)
_savegpr1_16:
    std r16,-128(r12)
_savegpr1_17:
    std r17,-120(r12)
_savegpr1_18:
    std r18,-112(r12)
_savegpr1_19:
    std r19,-104(r12)
_savegpr1_20:
    std r20,-96(r12)
_savegpr1_21:
    std r21,-88(r12)
_savegpr1_22:
    std r22,-80(r12)
_savegpr1_23:
    std r23,-72(r12)
_savegpr1_24:
    std r24,-64(r12)
_savegpr1_25:
    std r25,-56(r12)
_savegpr1_26:
    std r26,-48(r12)
_savegpr1_27:
    std r27,-40(r12)
_savegpr1_28:
    std r28,-32(r12)
_savegpr1_29:
    std r29,-24(r12)
_savegpr1_30:
    std r30,-16(r12)
_savegpr1_31:
    std r31,-8(r12)
    blr
end;


{* The _restgpr1_N routines restore the general registers from rN to r31.
 * When the routine is called, r12 must point to the start of the general
 * register save area.
 *}
label
  _restgpr1_15,
  _restgpr1_16,
  _restgpr1_17,
  _restgpr1_18,
  _restgpr1_19,
  _restgpr1_20,
  _restgpr1_21,
  _restgpr1_22,
  _restgpr1_23,
  _restgpr1_24,
  _restgpr1_25,
  _restgpr1_26,
  _restgpr1_27,
  _restgpr1_28,
  _restgpr1_29,
  _restgpr1_30,
  _restgpr1_31;

procedure _restgpr1_14; cdecl; public; assembler; nostackframe;
  asm
.globl _restgpr1_15
.globl _restgpr1_16
.globl _restgpr1_17
.globl _restgpr1_18
.globl _restgpr1_19
.globl _restgpr1_20
.globl _restgpr1_21
.globl _restgpr1_22
.globl _restgpr1_23
.globl _restgpr1_24
.globl _restgpr1_25
.globl _restgpr1_26
.globl _restgpr1_27
.globl _restgpr1_28
.globl _restgpr1_29
.globl _restgpr1_30
.globl _restgpr1_31

    ld r14,-144(r12)
_restgpr1_15:
    ld r15,-136(r12)
_restgpr1_16:
    ld r16,-128(r12)
_restgpr1_17:
    ld r17,-120(r12)
_restgpr1_18:
    ld r18,-112(r12)
_restgpr1_19:
    ld r19,-104(r12)
_restgpr1_20:
    ld r20,-96(r12)
_restgpr1_21:
    ld r21,-88(r12)
_restgpr1_22:
    ld r22,-80(r12)
_restgpr1_23:
    ld r23,-72(r12)
_restgpr1_24:
    ld r24,-64(r12)
_restgpr1_25:
    ld r25,-56(r12)
_restgpr1_26:
    ld r26,-48(r12)
_restgpr1_27:
    ld r27,-40(r12)
_restgpr1_28:
    ld r28,-32(r12)
_restgpr1_29:
    ld r29,-24(r12)
_restgpr1_30:
    ld r30,-16(r12)
_restgpr1_31:
    ld r31,-8(r12)
    blr
  end;

{* Each _savefpr_M routine saves the floating point registers from fM to f31,
 * inclusive. When the routine is called, r1 must point to the start of the
 * floating point register save area, and r0 must contain the value of LR on
 * function entry.
 *}
label
  _savefpr_15,
  _savefpr_16,
  _savefpr_17,
  _savefpr_18,
  _savefpr_19,
  _savefpr_20,
  _savefpr_21,
  _savefpr_22,
  _savefpr_23,
  _savefpr_24,
  _savefpr_25,
  _savefpr_26,
  _savefpr_27,
  _savefpr_28,
  _savefpr_29,
  _savefpr_30,
  _savefpr_31;

procedure _savefpr_14; cdecl; public; assembler; nostackframe;
  asm
.globl _savefpr_15
.globl _savefpr_16
.globl _savefpr_17
.globl _savefpr_18
.globl _savefpr_19
.globl _savefpr_20
.globl _savefpr_21
.globl _savefpr_22
.globl _savefpr_23
.globl _savefpr_24
.globl _savefpr_25
.globl _savefpr_26
.globl _savefpr_27
.globl _savefpr_28
.globl _savefpr_29
.globl _savefpr_30
.globl _savefpr_31

    stfd r14,-144(r1)
_savefpr_15:
    stfd r15,-136(r1)
_savefpr_16:
    stfd r16,-128(r1)
_savefpr_17:
    stfd r17,-120(r1)
_savefpr_18:
    stfd r18,-112(r1)
_savefpr_19:
    stfd r19,-104(r1)
_savefpr_20:
    stfd r20,-96(r1)
_savefpr_21:
    stfd r21,-88(r1)
_savefpr_22:
    stfd r22,-80(r1)
_savefpr_23:
    stfd r23,-72(r1)
_savefpr_24:
    stfd r24,-64(r1)
_savefpr_25:
    stfd r25,-56(r1)
_savefpr_26:
    stfd r26,-48(r1)
_savefpr_27:
    stfd r27,-40(r1)
_savefpr_28:
    stfd r28,-32(r1)
_savefpr_29:
    stfd r29,-24(r1)
_savefpr_30:
    stfd r30,-16(r1)
_savefpr_31:
    stfd r31,-8(r1)
    std r0, 16(r1)
    blr
  end;

{* The _restfpr_M routines restore the floating point registers from fM to f31.
 * When the routine is called, r1 must point to the start of the floating point
 * register save area.
 *}
label
  _restfpr_15,
  _restfpr_16,
  _restfpr_17,
  _restfpr_18,
  _restfpr_19,
  _restfpr_20,
  _restfpr_21,
  _restfpr_22,
  _restfpr_23,
  _restfpr_24,
  _restfpr_25,
  _restfpr_26,
  _restfpr_27,
  _restfpr_28,
  _restfpr_29,
  _restfpr_31;

procedure _restfpr_14; cdecl; public; assembler; nostackframe;
  asm
.globl _restfpr_15
.globl _restfpr_16
.globl _restfpr_17
.globl _restfpr_18
.globl _restfpr_19
.globl _restfpr_20
.globl _restfpr_21
.globl _restfpr_22
.globl _restfpr_23
.globl _restfpr_24
.globl _restfpr_25
.globl _restfpr_26
.globl _restfpr_27
.globl _restfpr_28
.globl _restfpr_29

    lfd r14,-144(r1)
_restfpr_15:
    lfd r15,-136(r1)
_restfpr_16:
    lfd r16,-128(r1)
_restfpr_17:
    lfd r17,-120(r1)
_restfpr_18:
    lfd r18,-112(r1)
_restfpr_19:
    lfd r19,-104(r1)
_restfpr_20:
    lfd r20,-96(r1)
_restfpr_21:
    lfd r21,-88(r1)
_restfpr_22:
    lfd r22,-80(r1)
_restfpr_23:
    lfd r23,-72(r1)
_restfpr_24:
    lfd r24,-64(r1)
_restfpr_25:
    lfd r25,-56(r1)
_restfpr_26:
    lfd r26,-48(r1)
_restfpr_27:
    lfd r27,-40(r1)
_restfpr_28:
    lfd r28,-32(r1)
_restfpr_29:
    ld r0, 16(r1)
    lfd r29,-24(r1)
    mtlr r0
    lfd r30,-16(r1)
    lfd r31,-8(r1)
    blr
  end;


procedure _restfpr_30; cdecl; public; assembler; nostackframe;
  asm
.globl _restfpr_31
    lfd r30,-16(r1)
_restfpr_31:
    ld r0, 16(r1)
    lfd r31,-8(r1)
    mtlr r0
    blr
  end;

{* Each _savevr_M routine saves the vector registers from vM to v31, inclusive.
 * When the routine is called, r0 must point to the word just beyound the end
 * of the vector register save area. On return the value of r0 is unchanged
 * while r12 may be modified.
 *}
{* commented out for now, unused
_savevr_20: addi r12,r0,-192
    stvx v20,r12,r0
_savevr_21: addi r12,r0,-176
    stvx v21,r12,r0
_savevr_22: addi r12,r0,-160
    stvx v22,r12,r0
_savevr_23: addi r12,r0,-144
    stvx v23,r12,r0
_savevr_24: addi r12,r0,-128
    stvx v24,r12,r0
_savevr_25: addi r12,r0,-112
    stvx v25,r12,r0
_savevr_26: addi r12,r0,-96
    stvx v26,r12,r0
_savevr_27: addi r12,r0,-80
    stvx v27,r12,r0
_savevr_28: addi r12,r0,-64
    stvx v28,r12,r0
_savevr_29: addi r12,r0,-48
    stvx v29,r12,r0
_savevr_30: addi r12,r0,-32
    stvx v30,r12,r0
_savevr_31: addi r12,r0,-16
    stvx v31,r12,r0
    blr
*}
{* The _restvr_M routines restore the vector registers from vM to v31. When the
 * routine is called, r0 must point to the word just beyound the end of the
 * vector register save area. On return the value of r0 is unchanged while r12
 * may be modified.
 *}
{* commented out for now, unused
_restvr_20: addi r12,r0,-192
    lvx v20,r12,r0
_restvr_21: addi r12,r0,-176
    lvx v21,r12,r0
_restvr_22: addi r12,r0,-160
    lvx v22,r12,r0
_restvr_23: addi r12,r0,-144
    lvx v23,r12,r0
_restvr_24: addi r12,r0,-128
    lvx v24,r12,r0
_restvr_25: addi r12,r0,-112
    lvx v25,r12,r0
_restvr_26: addi r12,r0,-96
    lvx v26,r12,r0
_restvr_27: addi r12,r0,-80
    lvx v27,r12,r0
_restvr_28: addi r12,r0,-64
    lvx v28,r12,r0
_restvr_29: addi r12,r0,-48
    lvx v29,r12,r0
_restvr_30: addi r12,r0,-32
    lvx v30,r12,r0
_restvr_31: addi r12,r0,-16
    lvx v31,r12,r0
    blr
*}

{******************************************************************************
                          Process start/halt
 ******************************************************************************}

procedure PASCALMAIN; external name 'PASCALMAIN';

procedure __libc_csu_init; cdecl; external;
procedure __libc_csu_fini; cdecl; external;

procedure __libc_start_main(r3,r4,r5,r6,r7,r8,r9: pointer); cdecl; external;

type
  tatexit_cb = procedure; cdecl;
procedure atexit(cb: tatexit_cb); cdecl; external;

procedure gmon_monstartup(text_start, text_stop: pointer); cdecl; external name '__monstartup';
procedure gmon_mcleanup; cdecl; external name '_mcleanup';

var etext: pointer; cvar; external;

var
  __fpc_ret: pointer;

procedure firstproc;
begin
end;

procedure save_argc_argv_envp_stkptr_fpcret_call_monstartup(argc: longint; argv, envp: ppchar; stkptr: pointer);
  var
    start: pointer;
  begin
    operatingsystem_parameter_argc:=argc;
    operatingsystem_parameter_argv:=argv;
    operatingsystem_parameter_envp:=envp;
    initialstkptr:=stkptr;
    __fpc_ret:=stkptr;
    start:=@firstproc;
{$ifndef _CALL_ELF or (_CALL_ELF = 1)}
    { load code address from function descriptor }
    start:=ppointer(start)^;
{$endif}
    gmon_monstartup(start,@etext);
    atexit(@gmon_mcleanup);
  end;


procedure _haltproc(exitcode: longint); cdecl; forward;

  var
    TOCSTART: pointer; external name '.TOC.';

label
  __data_start,
  data_start;

{*
 * This is our FreePascal main procedure which is called by
 * libc after initializing.
 *}
procedure main_stub(argc: longint; argv, envp: ppchar); cdecl; assembler; nostackframe;
  asm
{$if defined(_CALL_ELF) and (_CALL_ELF = 2)}
.L1:
    addis   r2,r12,(TOCSTART-.L1)@ha
    addi    r2,r2,(TOCSTART-.L1)@l
    .localentry main_stub, . - main_stub
{$endif}
    mflr    r0
    std     r0,16(r1)
    stdu    r1,-128(r1)

    mr      r6, r1
    bl      save_argc_argv_envp_stkptr_fpcret_call_monstartup

    bl  PASCALMAIN
    nop

    b   _haltproc
.section ".data"
.globl  __data_start
__data_start:
data_start:
.text
  end;

const
  start_addresses: record
    sda_base: pointer;
    main: pointer;
    libc_csu_init: pointer;
    libc_csu_fini: pointer;
  end
  = (sda_base: nil; {* was _SDA_BASE_  but not in 64-bit ABI }
     main: @main_stub;
     libc_csu_init: @__libc_csu_init;
     libc_csu_fini: @__libc_csu_fini
    );

procedure call_libc_start_main(r3,r4,r5,r6,r7,r8,r9: pointer); cdecl;
  begin
    __libc_start_main(r3,r4,r5,r6,r7,@start_addresses,r9);
    asm
      { not reached }
      { trap }
      .long 0xffe00008
    end;
  end;

{*
 * the real entry point for the program
 *}
procedure _start; cdecl; public; assembler; nostackframe;
  asm
{$if defined(_CALL_ELF) and (_CALL_ELF = 2)}
.L1:
    addis   r2,r12,(TOCSTART-.L1)@ha
    addi    r2,r2,(TOCSTART-.L1)@l
    .localentry _start, . - _start
{$endif}
    mr  r9,r1                   { save the stack pointer }

    { Set up an initial stack frame, and clear the LR.  }
    rldicr  r1,r1,0,59
    li      r0,0
    stdu    r1,-128(r1)
    mtlr    r0
    std     r0,0(r1)

    bl call_libc_start_main
    nop
  end;


procedure _haltproc(exitcode: longint); cdecl; public;
  var
    localret: pointer;
  begin
    localret:=__fpc_ret;
    { return via initial stack }
    asm
      lwa  r3, exitcode
      ld   r1, localret
      addi r1, r1, 128
      ld   r0, 16(r1)
      mtlr r0
      blr
    end;
  end;

